home *** CD-ROM | disk | FTP | other *** search
- Kurze Erklärung zum Make-Interpreter Aachen, Juli 1989
-
- Sinn und Aufgabe:
- Dieses Programm sollte die Unzulänglichkeiten meiner Kopmmandoshell
- lindern helfen. Es sollte in der Lage sein, vermittels weniger spezieller
- Befehle den Aufruf von Compiler, Linker, Editor in logischer Abfolge zu
- steuern. Dazu sind offenbar Mechanismen erforderlich, die Programmstarts
- ermöglichen und die Returnwerte zur Fehlerabfrage zur Verfügung stellen.
- Desweiteren benötigt man Zeitabfragefunktionen, die es erlauben, den
- Compiler wirklich nur dann starten zu müssen, wenn dies erforderlich ist.
- In der Regel wird ein Altersvergleich von Quell- und Objektdatei hierfür
- genügen. In der vorliegenden Version stehen neben einigen einfachen
- Anweisungen auch vier Strukturbefehle zur Verfügung, die recht
- übersichtliche Programmierung erlauben: IF/ELSE/ENDIF, WHILE/AGAIN (mit
- BREAK/CONTINUE), REPEAT/UNTIL und FOR-TO-STEP/NEXT. Alle sind bis zum
- Überlaufen des Stapels schachtelbar.
-
- Allgemeine Anweisungen und Syntaxregeln:
- Die zu bearbeitende Prozedur muß als ASCII-Datei abgespeichert sein und
- muß unbedingt auf ".mke" enden. Das vorliegende Programm wertet zunächst
- die Kommandozeile aus, wenn hier ein Argument vorhanden ist, welches durch
- ein führendes "-" gekennzeichnet ist, wird es als Name der Prozedur
- benutzt. Der Interpreter hängt nur noch ein ".mke" an. Ansonsten wird die
- erste Datei im aktuellen Verzeichnis, die auf ".mke" endet, als
- Makeprozedur abgearbeitet. Gibt es keine solche, dann erfolgt eine
- Fehlermeldung. Soll das Programm vom Desktop gestartet werden, muß es also
- in make.ttp umbenannt werden, oder es muß sichergestellt werden, daß sich
- maximal eine .mke Datei im aktuellen Verzeichnis befindet. Es empfiehlt
- sich aber die Verwendung einer Kommandoshell. Mit den übrigen
- Kommandozeilenargumenten wird wie folgt verfahren: sie werden der Reihe
- nach in die vordefinierten Stringvariablen kopiert und ihre Anzahl in der
- vordefinierten Variable DONE gespeichert. Sie können dann mit Hilfe der
- Stringfunktionen ausgewertet werden.
- Eine Zeile enthält beliebig viele Kommandos, die durch ";" getrennt
- werden. Das letzte Kommando einer Zeile braucht nicht mit ";" abgeschlossen
- werden. Ein Kommando kann mit beliebig vielen Spaces anfangen, um so eine
- strukturorientierte Schreibweise zu ermöglichen. Danach folgt entweder ein
- großgeschriebenes Schlüsselwort, oder das Kommando wird als zu startendes
- Programm interpretiert. Im ersten Fall wird bei korrekter Syntax der Befehl
- ausgeführt, im letzteren wird alles bis zum ersten Trennzeichen als Pfad
- und Dateiname des zu startenden Programmes interpretiert, der ganze Rest
- des Kommandos wird als Kommandozeile weitergereicht. Der Rückgabewert wird
- in der vordefinierten Variablen RC zur Verfügung gestellt. Hieraus ergibt
- es sich, daß relativ häufig die zunächst unsinnig erscheinende
- Fehlermeldung "PGM-Aufruf: ... existiert nicht" erfolgt, wenn man sich
- vertippt hat. Schlüsselworte müssen von den folgenden Argumenten stets
- durch mindesten ein Leerzeichen getrennt sein. Jede Prozedur muß durch END.
- beendet werden, alles was danach folgt wird ignoriert. Vor dem ersten
- Befehl dürfen eine beliebige Anzahl von Funktionen definiert werden.
-
- Beschreibung der einzelnen Kommandos und Funktionen:
- Vorbemerkungen:
- Pfadangabe sei im folgenden eine komplette Angabe von Pfad und Name einer
- Datei.
- Ausdruck sei eine Zeichenkette, die sich zu einem numerischen Wert
- berechnen läßt (stets long int). Zugelassen als Bezeichner sind
- Zeichenketten, die aus Klein- bzw. Großbuchstaben und dem Unterstrich
- bestehen. Lediglich die ersten acht Zeichen sind jedoch signifikant. Noch
- nicht belegte Variablen werden nach guter alter Basicmanier als Null
- angenommen. Zuweisung an Variablen: siehe "LET". Folgende Operatoren stehen
- zur Verfügung (in absteigender Rangfolge): ! (logisches not) ;
- *,/,%(modulo) ; +,- ; <,>,==,!=,<=,>= ; &&,& (logisches und bitweises und)
- ; ||,| (dito mit oder) . Die logischen Operatoren , sowie die
- Vergleichsoperatoren liefern Eins für wahr und Null für unwahr. Es dürfen
- verknüpft werden: Variablen, vordefinierte Funktionen, Zahlen- und
- Zeichenkonstanten. Ein Beispiel für letztere: 'A' liefert den Wert 65.
- Zahlenkonstanten können mit dem Symbol '$' versehen werden. Sie werden dann
- als Hexadezimalzahlen ausgewertet. Die Rangfolge kann wie üblich mit
- Klammern aufgehoben werden. Für die Pascal bzw. Modula-Programmierer, denen
- der Umgang mit der Darstellung von Wahrheitswerten durch Integerwerte sowie
- mit Bitoperatoren nicht vertraut ist, seien hier einige Beispiele für
- Ausdrücke angegeben:
- (3*3%7==2)+(7>5)*99 (liefert 100)
- (3*3%7==2)+(7>5)*99 && 98=='b' (liefert 1)
- !!!!(4*5+3/(5+8)) (liefert 1)
- 7&4|$10&ff (liefert 20)
- !0 (liefert 1)
-
- Einige Worte zu den Stringvariablen
- Anders als bei numerischen Variablen, sind bei der Stringverarbeitung
- sehr enge Grenzen gesetzt worden. Es steht ausschließlich das 16 Elemente
- umfassende Array FN[...] zur Verfügung. Es gibt keine Möglichkeit auf
- einzelne Zeichen zuzugreifen, die Verarbeitung beschränkt sich somit
- Zuweisung, Vergleich und Addition ganzer Zeichenketten. Da die
- Stringverarbeitung eigentlich nur zur Generierung und Auswertung von
- Kommandozeilen vorgesehen ist, habe ich ihre Länge auf 128 Zeichen
- begrenzt. Dadurch entfiel die Implementierung einer dynamischen
- Speicherverwaltung mit all ihren Problemen. Ein Begrif, den ich häufiger
- verwenden möchte, ist die Stringexpansion. Darunter verstehe ich folgendes:
- alles weitere des betreffenden Kommandos wird Zeichen für Zeichen als
- String betrachtet, lediglich wenn die Auswertung auf FN[<Ausdruck>] trifft,
- wird dies durch den Inhalt der entsprechenden Variablen ersetzt. Dieses
- Ersetzen kann unterdrückt werden, indem ein Hochzeichen (") eingefügt wird.
- Nun wird bis zum nächsten " alles orginalgetreu übernommen. Das " selbst
- kann also nicht in den String übernommen werden. Die Expansion bricht ab,
- wenn sie auf ein Komma oder eine Klammer-zu trifft. Sollen diese Zeichen in
- den String, dann müssen sie in Hochzeichen gesetzt werden. Die
- Stringexpansion wird bei einigen stringverarbeitenden Funktionen
- angewendet.
-
- Einzelne Kommandos (nicht mit Funktionen zu verwechseln, welche nur in
- Ausdrücken stehen dürfen und durch Funktionsklammern kenntlich sind):
- *** PUTS ***: Ausgabe an Stdout (vergleiche OUT_TO). Die Argumente können
- in beliebiger Anzahl vorhanden sein und müssen durch Kommata getrennt
- werden. Zulässige Argumente sind insbesondere in Hochzeichen
- eingeschlossene Zeichenketten, oder Ausdrücke, welche dann natürlich vorher
- ausgewertet werden. Vor die Ausdrücke darf ein "h:" gesetzt werden. In
- diesem Fall erfolgt die Ausgabe im Hexadezimalsystem (wie für Hexzahlen
- üblich als vorzeichenlose Zahl). Auch ein "d:" für Dezimalausgabe ist
- zulässig, wird aber ohnehin als default angenommen. Ein "c:" bewirkt die
- Ausgabe als Zeichen, z.b. kann man mit PUTS c:27, "E" den Bildschirm
- löschen. Da die 27 als Zeichen ausgegeben wird, kann sie vom VT 52 Emulator
- als Escape erkannt werden. Somit stehen alle Steuersequenzen zur Verfügung.
- Als weiteres Argument darf FN[...] geschrieben werden. Der entsprechende
- String wird ausgegeben. Ein spezielles Argument ist DATE, welches Datum und
- Uhrzeit in üblicher Formatierung ausgibt. DATE ist aber keineswegs eine
- vordefinierte Variable, kann also wirklich nur von PUTS erkannt werden.
- Wird hinter das letzte Argument ein Punkt gesetzt, dann wird die Ausgabe
- mit CR/LF abgeschlossen, ansonsten erfolgt die Ausgabe des nächsten PUTS
- direkt hinter dem zuletzt ausgegebenen Zeichen.
- *** SPACES ***: Ein Argument vom Typ Ausdruck. Es werden soviele
- Leerzeichen ausgegeben wie der Wert des Ausdrucks modulo 80.
- *** PUTC, SPACEC ***: wie die beiden vorhergehenden, jedoch erfolgt die
- Ausgabe grundsätzlich auf dem Bildschirm.
- *** LET ***: Hierdurch wird eine Zuweisung an eine Variable vorgenommen.
- Die auf LET folgende Zeichenkette muß wie folgt aufgebaut sein: gültiger
- Variablenname (s.o.), evtl. Leerzeichen, "=", evtl. Leerzeichen,
- fehlerfreier Ausdruck. Die Anzahl der möglichen Variablen ist nur durch den
- Speicherplatz begrenzt. Da hier aber keine Abfrage erfolgt, sollte man
- nicht mehr als einige Tausend Variablen einführen, sonst gibt's einen
- 2-Bomben-Bus-Error. Bereits belegte Variablen werden bei einer Zuweisung
- mit dem neuen Wert überschrieben. Nicht belegte Variablen werden als Null
- angenommen. Vorsicht: bei allen mir bekannten Programmiersprachen kann auf
- eine Kennzeichnung einer Zuweisung verzichtet werden, daher vergißt man das
- LET allzuleicht. Dies führt dann zur Fehlermeldung "Fehler in Dateiname",
- da die Zuweisung als Programmaufruf interpretiert wird.
- *** LONGPOKE ***: Argumente sind eine Adresse und durch Komma getrennt
- ein Wert. Dieser wird in der angegebenen Adresse abgespeichert. Es wird im
- Supervisormode gearbeitet, daher Vorsicht vor Abstürzen! Es muß unbedingt
- vermieden werden, eine ungerade Adresse anzusprechen, um den
- 3-Bomben-Adress-Error zu vermeiden. Vergleiche auch die Funktion
- LONGPEEK().(s.u.). Diese Funktionen sollen die Systemvariablen zugänglich
- machen.
- *** DEL ***: Argument ist eine Pfadangabe. Die entsprechende Datei wird
- gelöscht wenn möglich, RC erhält den Wert 1 im Erfolgsfalle, sonst 0. DEL
- wertet Wildcards aus, es werden also alle auf das Muster passende Dateien
- gelöscht.
- *** GEM ***: Dieses Schlüsselwort am Beginn einer Zeile, die ansonsten
- genau den Regeln eines Programmaufrufs genügt (siehe unten), bewirkt, daß
- vor dem eigentlichen Programmstart noch der Bildschirm mit dem
- Desktopraster überschrieben wird. Einige Gemprogramme, die dies leider
- nicht selbst besorgen, erfordern also die Benutzung des Befehls.
- *** RETURN ***: unbedingter Abbruch der Prozedur, wenn im Hauptprogramm
- benutzt.
- *** GOSUB, FUNC, ENDFUNC ***: siehe Funktionen.
- *** CHG_EXT ***: erstes Argument ist eine Stringvariable. Ihr Inhalt wird
- als Pfadangabe interpretiert. Gelingt dies nicht, gibt es eine
- Fehlermeldung. Ansonsten wird die Dateierweiterung der spezifizierten Datei
- gegen das zweite, durch Komma abgetrennte Argument ausgetauscht. Gab es
- zuvor keine Erweiterung, so wird noch ein Punkt eingefügt. Das zweite
- Argument besteht lediglich aus ein bis drei Buchstaben, nicht in
- Hochzeichen eingeschlossen. Sinn dieser Anweisung ist es, abhängige Dateien
- zu ermitteln. Es können z.B. mit SEARCH *.C alle Quelltextdateien gesucht
- werden und mit Hilfe von CHG_EXT und DATE_OF ein Altersvergleich mit den
- gleichnamigen .O Dateien vorgenommen werden.
- *** SHOWC ***: Argument ist eine Pfadangabe. Die entsprechende Datei wird
- unabhänig von der Ausgabeumlenkung auf dem Bildschirm ausgegeben. Typische
- Anwendung dürfte die Ausgabe der Fehlerdatei nach dem Übersetzungvorgang
- sein.
- *** SHOWS ***: wie SHOWC, aber Ausgabe über Standardkanal, also
- umlenkbar.
- *** OUT_TO ***: Die umlenkbare Ausgabe wird in die als Argument
- angegebene Datei gelenkt. Umlenkbar sind sowohl PUTS, wie auch SPACES und
- die Ausgaben der meisten der gängigen .tos oder .ttp Programme.
- Erforderlich ist die Umlenkung, weil kein Fehlerkanal im TOS besteht und
- die Compiler somit gezwungen sind, alle Fehlermeldungen über den
- Standardkanal auszugeben. Man lenkt die Ausgabe also vor dem Aufruf des
- Compilers um, etwa in "fehler.err", und kann die Liste der Fehler nachher
- im Editor verwenden. Für die Rückumlenkung auf die Konsole gibt es den
- vordefinierten Dateinamen "con". Die vordefinierten Namen für Drucker und
- serielle Schnittstelle lauten "printer" und "aux". Prinzipiell könnte
- OUT_TO im Zusammenhang mit SHOWS zum Kopieren von Dateien verwendet werden,
- jedoch erfolgt eine Aufbereitung der Zeilenendezeichen, so daß dies
- grundsätzlich auf reine ASCII-Dateien beschräbnkt bleiben sollte.
- *** SEARCH ***: Argument ist eine Pfadangabe, die in der Regel Wildcards
- enthalten dürfte. Es werden alle Dateinamen, die auf das angegebene Muster
- passen, der Reihe nach in die Stringvariablen kopiert. Dabei wird mit FN[0]
- angefangen. Die gefundene Anzahl wird in DONE abgespeichert. Es darf ein
- durch Komma abgetrennter Ausdruck folgen, der das Dateiattribut angibt, das
- die gefundene Datei besitzen sollte
- *** GETSTR ***: Argument ist eine Stringvariable. Es wird ein String von
- der Tastatur eingelesen und an die Variable zugewiesen. Vorsicht: im
- allgemeinen wird man wohl Dateinamen einlesen. Dies ist insofern kritisch,
- als keine Möglichkeit besteht, Tippfehler zu tolerieren. Wird nämlich eine
- Funktion aufgerufen, die eine Pfadangabe erwartet, erfolgt sofortiger
- Abbruch der Prozedur, wenn "Fehler in Dateiname" vorliegt.
- *** STRCPY ***: erstes Argument ist eine Stringvariable. Das zweite kann
- ein in Hochzeichen eingeschlossener String, oder eine zweite Stringvariable
- sein. Es erfolgt keine Expansion. Die Argumente werden durch Komma
- getrennt. Der zweite String wird an den ersten zugewiesen.
- *** STRCAT ***: Für die Argumente gilt das gleiche wie bei STRCPY. Der
- zweite String wird an den ersten angehängt, das Ergebnis steht im ersten.
- *** # ***: Einleitung eines Kommentars. Alles bis zum nächsten
- Kommandotrennzeichen wird überlesen. Auch auf dieses Kommando muß zunächst
- ein Leerzeichen folgen.
- *** WHILE, AGAIN ***: abweisende Schleife. Ist der Ausdruck hinter dem
- WHILE von Null verschieden, dann werden die Befehle zwischen WHILE und
- AGAIN solange ausgeführt, bis sich dies geändert hat. Ansonsten wird hinter
- dem korrespondierenden AGAIN fortgefahren. Wird in der Schleife ein BREAK
- gefunden, dann wird die Schleife unmittelbar an dieser Stelle verlassen und
- hinter dem korrespondierenden AGAIN fortgefahren. Bei CONTINUE wird sofort
- mit der nächsten Überprüfung der WHILE-Bedingung fortgefahren. Diese beiden
- Befehle werden nur von der WHILE-Schleife erkannt, man darf sie also nie in
- den anderen benutzen, auch dann nicht, wenn diese von einer WHILE-Schleife
- umschlossen werden. Dahingegen dürfen die Befehle in einer beliebig
- geschachtelten Anzahl von IFs benutzt werden, es wird dann die
- nächstumschließende WHILE-Schleife angesprochen.
- *** REPEAT, UNTIL ***: nicht abweisende Schleife, UNTIL hat einen
- Ausdruck als Argument. Die Kommandos zwischen REPEAT und UNTIL werden
- ausgeführt bis der Ausdruck wahr (also ungleich Null) wird, aber mindestens
- einmal.
- *** IF, ELSE, ENDIF ***: IF hat ein Argument, einen Ausdruck. Ist dieser
- ungleich Null, also wahr, dann werden die Anweisungen zwischen IF und ELSE
- ausgeführt, sonst diejenigen zwischen ELSE und ENDIF. ELSE darf weggelassen
- werden. Vorsicht bei IF, ELSE IF, ... , ELSE IF, ENDIF Konstruktionen: da
- ein ELSE und das folgende IF gewöhnlich in derselben Zeile stehen, müssen
- sie durch Semikolon getrennt werden. Auch müssen in der letzten Zeile der
- Konstruktion soviele ENDIFs stehen (auch durch ; getrennt), wie IFs
- verwendet wurden!
- *** FOR, TO, STEP, NEXT ***: abweisende Schleife. Zwischen FOR und TO muß
- eine Zuweisung erfolgen, hier jedoch ohne das Schlüsselwort LET. Nach dem
- FOR steht ein Ausdruck. Folgt nun das optionale STEP, dann muß danach als
- letztes Element des Kommandos noch ein Ausdruck folgen. Fehlt STEP, wird
- als Schrittweite Eins genommen, sonst das Ergebnis des Ausdrucks. Die
- Schleife wird solange ausgeführt, bis die Variable, die in der Zuweisung
- verwendet wurde, echt größer ist als der Ausdruck hinter TO.
- (Schrittweite>0 vorausgesetzt, ansonsten analog) Sehr wichtig: Die
- Auswertung der Ausdrücke erfolgt nur vor dem Eintritt in die Schleife,
- daher kann sich die Schrittweite über einen Laufzeitausdruck nicht
- verändern! Mit NEXT wird die Schleife abgeschlossen.
-
- Vordefinierte Funktionen:
- Diese Funktionen liefern numerische Werte und können somit (nur!) in
- Ausdrücken verwendet werden.
- *** TIME_F() ***: liefert die Systemzeit in 1/200s - Schritten ("fast").
- Nach dem ersten Aufruf erhält man einen zufälligen Wert, danach immer den
- Wert, der der seit dem letzten Aufruf vergangenen Zeit entspricht. Soll ein
- Programmlauf gestoppt werden, muß also vorher TIME_F() einmal mit GOSUB
- aufgerufen oder einer Dummy-Variablen zugewiesen werden.
- *** TIME_S() ***: liefert die Systemzeit in Sekunden ("slow"). Hierbei
- ist garantiert, daß es sich um die absolute Sytemzeit handelt, d.h. durch
- Divisionen und Subtraktionen läßt sich die Uhrzeit gewinnen. Die beiden
- Funktionen können zum Stoppen von Programmlaufzeiten benutzt werden.
- *** DATE_OF(Pfadangabe) ***: liefert das Datum der Dateierstellung der
- als Argument übergebenen Datei. Existiert sie gar nicht, so wird Null
- zurückgegeben. Dies entspricht dem höchstmöglichen Alter, das eine Datei
- haben kann. Mit dieser Funktion kann man die Abhängigkeiten von Quelltexten
- von einander untersuchen und überflüssige Compileraufrufe vermeiden. Das
- Argument zwischen den Klammern wird zuvor expandiert, es können also
- Stringvariablen verwendet werden.
- *** GETCHAR() ***: wartet auf eine Tastatureingabe und liefert den
- ASCII-Kode der Taste. Es wird kein Echo ausgegeben, dies muß gegebenenfalls
- durch PUTS erfolgen.
- *** SCANCHAR() ***: Fragt den Tastaurpuffer ab. Liefert Null, wenn keine
- Taste gedrückt wurde und wartet nicht auf eine Eingabe. Ansonsten wie
- GETCHAR().
- *** LONGPEEK(Ausdruck) ***: Der Ausdruck wird als Adresse interpretiert
- ab der ein Langwort gelesen wird. Dieses ist der Rückgabewert der Funktion.
- Vergleiche Bemerkungen zu LONGPOKE.
- *** STRCMP ***: erstes Argument ist eine Stringvariable, das zweite durch
- Komma getrennt, ein String oder eine zweite Stringvariable. Im ersten Falle
- muß dieser in Hochzeichen eingeschlossen sein, es erfolgt keine Expansion.
- Die beiden Strings werden verglichen. Ergebnis ist die Differenz der
- ASCII-Werte des ersten Zeichens, in dem sie sich unterscheiden. Gleichheit
- von Strings muß also durch !STRCMP(...) untersucht werden.
- *** GETINT ***: Es wird ein numerischer Wert eingelesen. Dieser darf als
- beliebiger Ausdruck eingegeben werden, der Ausdruck wird nach den bekannten
- Regeln ausgewertet und liefert den Rückgabewert der Funktion. Der Ausdruck
- darf übrigens durchaus wieder die Funktion GETINT enthalten.
-
- Selbstdefinierte Funktionen
- Vor dem eigentlichen Programm dürfen Funktionen definiert werden. Eine
- Funktionsdefinition beginnt durch das Schlüsselwort FUNC gefolgt vom Namen
- der Funktion und symbolischen Funktionsklammern. Für die Funktionsnamen
- gilt das gleiche wie für Variablennamen. Die Funktionsdefinition endet mit
- dem Schlüsselwort ENDFUNC. Alle Anweisungen zwischen FUNC und ENDFUNC
- werden später bei Funktionsaufrufen ausgeführt. Die Anzahl der Funktionen
- ist nur durch den Speicherplatz begrenzt. Der Aufruf kann auf zwei Arten
- erfolgen: durch "GOSUB Fktname()" wird die Funktion direkt aufgerufen, ihr
- Rückgabewert geht verloren. Wenn die selbstdefinierte Funktion hingegen als
- Teil eines Ausdrucks benutzt wird, kann ihr Rückgabewert verarbeitet oder
- an eine Variable zugewiesen werden. In den Funktionsklammern kann ein
- Ausdruck als Parameter übergeben werden. Dieser steht innerhalb der
- Funktion als Variable mit dem Namen der Funktion zur Verfügung. Dieser
- Variablen kann in der Funktion ein neuer Wert zugewiesen werden. Dies ist
- dann der Rückgabewert der Funktion, der im Ausdruck weiterverarbeitet wird.
- Durch diesen Mechanismus wird eine Rekursion unterbunden, die im Prinzip
- möglich wäre, jedoch äußerst sinnlos, da keine lokalen
- Speichermöglichkeiten bestehen.
-
- Programmaufruf:
- Nun endlich zur eigentlichen Aufgabe des Programms. Wird die führende
- Buchstabensequenz eines Kommandos nicht als Schlüsselwort erkannt, dann
- wird versucht, sie als Pfadangabe zu identifizieren. Gelingt dies nicht,
- dann gibt es eine Fehlermeldung, ansonsten wird die entsprechende Datei
- gestartet. Der gesamte Rest des Kommandos wird expandiert und dem Programm
- als Kommandozeile mit auf den Weg gegeben. Nach dem Programmstart wird der
- vordefinierten Variablen "RC" der Returnwert des Programmes zugewiesen. Gab
- es einen Fehler (z.B. -33 für File not found), dann erhält "RC" diesen
- Fehlerwert.
-
- Sonstiges:
- Auf einen besonders tückischen Fehler möchte ich hier noch extra
- hinweisen: Da Kommandozeilen nicht länger als 128 Bytes werden dürfen, kann
- es hier zu Fehlermeldungen bei rein syntaktisch völlig korrekten Kommandos
- kommen. Gerade beim Aufruf des Linkers hat man die 128 Byte schnell voll.
- Auf der Diskette befinden sich einige Demos für Makeprozeduren, die zum
- größten Teil nur als Anwendungsbeispiele der Befehle dienen sollen.
- Lediglich make.mke ist ein echtes Make. Hiermit habe ich in der letzten
- Phase der Programmentwicklung schon zufriedenstellend am vorliegenden
- Programm arbeiten können.
-
- ***** Erweiterungen der Version 2.0 ***** Aachen, im Mai 1992
-
- Nach längerer Zeit sind nun einige sinnvolle Erweiterungen vorgenommen
- worden, die teilweise erkannte Mißstände beheben, zum Teil auch einfach
- neue Funktionen zur Verfügung stellen. Die Kompatibilität zur ersten
- Version konnte nicht gewahrt werden, jedoch ist die Ähnlichkeit der
- Prozeduren groß.
- Vorweg eine nützliche Kleinigkeit: Prozeduren können nun durch
- gleichzeitige Betätigung von Control, Alternate und Shift-links
- unterbrochen werden, allerdings wird diese Kombination nur nach jedem
- zehnten Befehl abgefragt.
- Auffälligste Änderung: Es können nun auch Kleinbuchstaben bei den
- Schlüsselworten verwendet werden. Nach wie vor müssen diese von einem
- Leerzeichen gefolgt werden, um erkannt zu werden. Es ist verführerisch, nun
- in C-Syntax "if(a<B) ... " zu schreiben, dies wird aber nicht erkannt.
- Groß/Kleinschreibung wird bei Bezeichnern aber nach wie vor unterschieden,
- daher muß man auch noch immer RC schreiben. DONE existiert nicht mehr.
- Wichtigste Änderung: Es wird jetzt der Shell-Pointer-Mechanismus genutzt.
- Wenn MAKE von einer Kommandoshell gestartet worden ist, dann kann diese aus
- einer Prozedur aufgerufen werden. Wenn beim Programmaufruf die Datei nicht
- gefunden wird, dann wird die ganze Zeile an die Shell übergeben, wenn eine
- solche vorhanden ist. Es steht somit der ganze Befehlsumfang der Shell zur
- Verfügung. Expliziter Aufruf der Shell mit einem Befehl ist auch mit Hilfe
- der Funktion system() möglich (s.u.). In diesem Fall wird der Returncode
- der Shell als Funktionswert, in jenem über RC bekanntgemacht. Wird, wie
- üblich, der Programmaufruf-Mechanismus verwendet, dann muß das
- Shellkommando allerdings syntaktisch den Regeln für Dateinamen entsprechen.
- Ein kleiner Nachteil ergibt sich: syntaktisch falsch geschriebene Kommandos
- werden an die Shell weitergereicht, von dieser untersucht und i.a. nicht
- erkannt. Die entsprechende Fehlermeldung wird jetzt natürlich nicht von der
- Prozedur explizit abgefragt, und damit wäre die fehlerhafte Zeile einfach
- überlesen worden. Um dies zu vermeiden, führen Shell-Fehlermeldungen stets
- zu einer Warnung.
- Praktischste Änderung: Nun wurden richtige Stringvariablen eingeführt.
- Ihre Länge ist nach wie vor begrenzt, da sie ja zur Erzeugung von
- Kommandozeilen und Programmnamen dienen sollen. Das Stringfeld FN[] ist nun
- hinfällig geworden. Stringvariablen sind durch ein $ hinter dem Namen zu
- kennzeichnen. Sie können, wie nunmehr alle Variablen, durch eine
- Indizierung der Form '['<Ausdruck>']' modifiziert werden. Allerdings
- verkürzt sich die signifikante Länge der Namen bei Feldern auf fünf
- Zeichen. Der Indexbereich geht von etwa -1600 bis +1600. Der Typ einer
- Variablen wird durch die jeweils letzte Zuweisung festgelegt, es existieren
- also nicht zugleich 'var' und 'var$', hingegen sind gewöhnliche und
- indizierte Variablen des gleichen Namens nebeneinander möglich. Bei
- Typfehlern wird die Prozedur abgebrochen. Nicht zugewiesene Variablen sind
- Null (numerisch) oder leer (String). Werden Stringvariablen in numerischen
- Ausdrücken verwendet, dann werden sie ausgewertet, als wenn ihr Inhalt Teil
- der Prozedur wäre, jedoch bilden sie dabei eine vollständig geschlossene
- Klammerebene. 'Syntaxfehler im Ausdruck' ist nun höchstwahrscheinlich auf
- einen nicht arithmetisch interpretierbaren Stringinhalt zurückzuführen! Die
- Kommandozeilenargumente liegen jetzt im Feld argv$[1..argc], nach
- Prozedurstart gibt argc also die Anzahl der Kommandos an. Dazu kommt noch
- der Name der Prozedur in der Variable argv$[0].
- Bei der Stringexpansion hat sich durch die neuen Variablen einiges
- geändert. ""-Zeichen schließen einen Teil ein, der unverändert übernommen
- wird, ''-Zeichen dürfen den gesamten zu expandierenden Bereich umfassen, um
- definierte Enden festzulegen. Insbesondere am Zeilenende verbergen sich oft
- ungewollte Leerzeichen, die normalerweise mit in den String übernommen
- würden, andererseits werden ohne Hochzeichen führende Leerzeichen
- ausgelassen; beidem kann man hiermit entgegenwirken. ' a$ ' ergibt einen
- String, der vor und nach dem Inhalt von a$ genau ein Leerzeichen
- beinhaltet. Ist das erste Zeichen kein ', dann haben weitere ' keine
- spezielle Bedeutung, ansonsten bricht die Expansion beim nächsten ' ab.
- Folgen dann noch weitere Zeichen in der Zeile, gibt es einen Syntaxfehler.
- /,",' kann man durch //, /", /' in den expandierten String einfügen, in
- konstanten Strings (durch "" erzeugt) ist davon nur /" wirksam. / dient
- auch als Trennsymbol: soll vor einem Variableninhalt ohne Leerzeichen ein
- Buchstabe stehen, dann geht es nicht ohne weiteres: 'halloa$' würde nicht
- als "hallo"a$ interpretiert werden, sondern es würde nach einer
- Stringvariable des Namens halloa gesucht werden. So muß man schreiben:
- 'hallo/a$'. (Auch "hallo"a$ ist nicht ganz zufriedenstellend, weil hallo
- dann nicht als numerische Variable erkannt werden kann.) Alle
- Nichtbuchstaben sind gleichfalls Trennzeichen. Numerische Variablen werden
- durch ihren Inhalt als vorzeichenbehaftete Zahl ersetzt, Ausdrücke werden
- nicht ausgewertet. Man sollte wegen der vielfältigen
- Expansionsmöglichkeiten tunlichst auf "" zurückgreifen, um ungewollte
- Effekte zu vermeiden. Man hat ja leicht ein paar kurze Variablennamen wie
- a, i, t etc., die expandiert würden, sofern sie von Trennzeichen umgeben
- sind. An Variablen, die expandiert werden sollen, muß in der Vergangenheit
- bereits etwas zugewiesen worden sein, hier gilt natürlich nicht das zuvor
- Gesagte über Apriori-Inhalte von Variablen. Werden zu expandierende
- Stringvariablen in geschweifte Klammern eingeschlossen, werden sie selbst
- wieder über die gleiche Expansionsfunktion ausgewertet, jedoch mit kleinen
- Unterschieden. '' hat nun keine besondere Bedeutung, es werden alle Zeichen
- des Stringinhaltes übernommen. Auch wird die Expansion garantiert erst am
- Inhaltsende abgebrochen, sonst wird je nach Kontext z.B. bei einem Komma
- oder bei ')' (Endekennung eines Funktionsparameters) o.ä. abgebrochen.
- Generell werden keine Variablennamen erkannt, die nur zur Hälfte in einem
- Stringinhalt stehen und zur anderen in der Prozedurzeile. Zyklische
- Verschachtelungen sind sowohl bei der Ausdrucksauswertung als auch bei der
- Stringexpansion verboten und führen zur Fehlermeldung. Gewöhnlich wird
- jedoch einfach der Inhalt der Stringvariablen unverändert in den
- Ergebnisstring eingefügt. Die Klammer selbst kann durch {{ in den
- Ergebnisstring eingefügt werden.
- Im Gegensatz zu früher werden nun die Stringparameter aller Funktionen
- über die Expansion gewonnen, solange es sich nicht um "lvalues" handelt,
- also um Angaben von Variablen, an die dann zugewiesen werden soll.
- Eine schreibgeschützte Variable version (oder VERSION) liefert die
- Versionsnummer des Interpreters als dreistellige Zahl.
-
- Geänderte Kommandos:
- *** PUTS, PUTC ***: Hier wird die Stringexpansion nach wie vor nicht
- angewandt, da auch Ausdrücke ausgewertet werden sollen. Als weiteres
- mögliches Argument kommen nun die Stringvariablen hinzu, aber eben jeweils
- nur einzeln. Das schon früher funktionierende d: vor einem Ausdruck ist nun
- nicht mehr bedeutungslos: vor eine Stringvariable gesetzt bewirkt es, daß
- diese als dezimal anzuzeigender Ausdruck ausgewertet wird. Fängt ein
- Ausdruck mit einer Stringvariablen an, dann ist es jetzt sogar notwendig,
- ein d: davorzusetzen, da sonst nach Ausgabe der Variablen eine
- Fehlermeldung erfolgte. Desweiteren hat sich DATE in DATE$ verwandelt.
- *** SEARCH ***: es können nun mehrere Pfade angegeben werden, wobei
- Stringexpansion erfolgt. Jedem Pfad kann ein optionales Suchattribut
- zugeordnet werden, das ihm nun jedoch in runden Klammern folgen muß, das
- Komma dient jetzt der Trennung der verschiedenen Pfade. Auch muß nun
- mitgeteilt werden, wo das Ergebnis abgespeichert werden soll. Dazu wird der
- neu eingeführte erste Parameter verwendet. Er muß syntaktisch eine nicht
- indizierte Stringvariable darstellen. Anschließend wird das zugehörige Feld
- die gefundenen Dateinamen im Indexbereich 1...max enthalten. Der Wert von
- max wird in der numerischen Variable desselben Namens abgelegt.
- (gewöhnliche und Feldvariablen desselben Namens können gleichzeitig
- existieren)
- *** OUT_TO ***: Statt 'printer' und 'con' können nun auch die Bezeichner
- 'prn' und 'stdout' als gleichwertige Angaben benutzt werden.
- *** GETSTR ***: Es können nun auch Leerzeichen eingegeben werden.
- Einziger Operand ist eine Stringvariable. Existierte eine numerische
- Variable des Namens, dann erfährt sie jetzt eine Typumwandlung.
- *** LET ***: Es können nun auch Zuweisungen an Stringvariablen
- vorgenommen werden, die die lästige Verwendung von STRCPY und STRCAT
- überflüssig machen. Dabei wird STRCPY durch '=', STRCAT durch '+='
- ausgeführt. Die rechte Seite der Zuweisung wird durch Stringexpansion
- gewonnen. Bei += wird die Variable der linken Seite auf "" gesetzt, wenn
- sie vorher als numerische Variable oder gar nicht existierte. Bei = bleibt
- die Variable der linken Seite zur Zeit der Berechnung der rechten Seite
- erhalten, auch wenn es vorher eine numerische Variable war.
- *** STRCAT, STRCPY ***: Existieren nicht mehr.
- *** LONGPOKE ***: Existiert nicht mehr, wurde in poke umgewandelt.
-
- neue Kommandos:
- *** warn_ver ***: als Argument folgt ein Ausdruck, der eine
- Make-Versionsnummer (dreistellig, mal hundert) angibt. Ist diese höher, als
- die des verwendeten Interpreters, dann wird die Prozedur mit einem
- entsprechenden Hinweis abbgebrochen.
- *** warn_sys ***: die Prozedur wird mit einem entsprechenden Hinweis
- abgebrochen, wenn keine Kommandoshell installiert ist.
- *** exit ***: Argument ist numerisch. Ist der Wert gleich Null, dann
- passiert nichts. Bei Werten ungleich Null wird die Prozedur unbedingt
- beendet. Ist der Wert größer Null, dann wird Null zurückgegeben, sonst der
- Wert selbst.
- *** dir ***: es folgt die Angabe einer Stringvariablen, ein Komma und
- eine Pfadangabe. Dann wird eine Dateiauswahlbox angezeigt, die wie üblich
- bedient wird. Der Stringvariablen wird der Name der ausgewählten Datei
- zugewiesen. Bei Wahl von 'Abbruch' wird ihr Inhalt nicht geändert. An RC
- wird die boolsche Aussage zugewiesen, ob eine Auswahl erfolgte.
- *** poke ***: wie das frühere LONGPOKE, jedoch mit drittem, numerischen,
- durch Komma abgetrenntem Argument, das angibt, ob Byte-, Wort- oder
- Langwortzugriff erfolgen soll. Es erfolgt eine Wortgrenzenprüfung, jedoch
- ist trotzdem höchste Vorsicht geboten, da im Supervisormodus gearbeitet
- wird. Bytes werden vorzeichenlos behandelt, Wort und Langwort sind
- vorzeichenbehaftet. Das dritte Argument wird so ausgewertet, daß man
- Zeichenkonstanten übergeben kann, also z.B. 'b' oder 'B' für Byte-Zugriff.
- Vergleiche peek().
-
- geänderte Funktionen:
- *** LONGPEEK() ***: Existiert nicht mehr, wurde durch peek() ersetzt.
-
- neue Funktionen:
- *** istomake() ***: Erstes Argument: es wird festgestellt, ob diese Datei
- wirklich jünger ist, als alle weiteren angegebenen. Dann liefert die
- Funktion false, sonst zeigt sie an, daß z.B. compiliert werden muß. Es
- folgt ein Doppelpunkt und mindestens ein weiteres Argument. Weitere
- Abhängigkeiten können durch Kommas getrennt angegeben werden. Alle
- Argumente werden über Stringexpansion gewonnen und müssen als fehlerfreie
- Pfadangaben interpretierbar sein.
- *** exists() ***: Ein durch Expansion gewonnenes Argument, das als
- Pfadangabe interpretiert wird. Die Funktion liefert die boolsche Aussage,
- ob die entprechende Datei existiert.
- *** system() ***: Ein durch Expansion gewonnenes Argument, das im ganzen
- als Kommando an die Shell übergeben wird, wenn eine solche vorhanden ist,
- sonst wird die Prozedur abgebrochen. Funktionswert ist der Returncode der
- Shell. Eine Warnung bei Fehlermeldungen der Shell unterbleibt hier aus
- einsichtigen Gründen. Wird kein Argument übergeben, d.h. system()
- geschrieben, dann liefert die Funktion eine Aussage über das Vorhandensein
- einer Shell.
- *** toupper(), tolower() ***: Ein numerisches Argument, dessen Groß-,
- bzw. Kleinschreibung seiner Interpretation als Zeichen zurückgegeben wird,
- wenn diese existiert. Sonst wird das Argument unverändert zurückgegeben.
- *** extcmp() ***: Zwei Stringargumente, deren erstes einen Pfad angibt.
- Das zweite darf maximal drei zusammenhängende Zeichen (ohne '.')
- beinhalten. Ohne auf Groß-/Kleinschreibung zu achten, wird wie bei STRCMP
- der String mit der Erweiterung des Dateinamens des Pfades verglichen.
- *** strcmpnc() ***: Arbeitet genau wie STRCMP() jedoch wird nicht auf
- Groß-/Kleinschreibung geachtet.
- *** peek() ***: hat nun zwei numerische, durch Komma getrennte Argumente,
- wobei das letzte angibt, welche Wortbreite gelesen werden soll. Das erste
- ist unverändert die Adresse, an der gelesen wird. Es gelten alle
- Bemerkungen, die zu poke gemacht wurden.
-